package gx.hec.sshbackup;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.Banner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.system.ApplicationHome;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.Scanner;

@SpringBootApplication
public class SshBackupApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(SshBackupApplication.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.run(args);
    }

    private String username = "";
    private String password = "";
    private String encUsername = "";
    private String encPassword = "";
    private static final String key = "sshbackuppasswor";
    private static final String initVector = "sshbackupvectori";
    private static final int maxMore = 100;
    private static final int exitTime = 15;
    private static final int bufSize = 4096;

    private String encrypt(String value) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString((encrypted));
    }

    private String decrypt(String encrypted) throws Exception {
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));
        return new String(original);
    }


    private void inputLogin(File loginFile) throws Exception {
        Console cons = System.console();
        if (cons != null) {
            System.out.print("请输入SSH登录账号:");
            this.username = cons.readLine();
            System.out.print("请输入SSH登录密码:");
            char[] chars = cons.readPassword();
            this.password = String.valueOf(chars);
        } else {
            Scanner sc = new Scanner(System.in);
            System.out.print("请输入SSH登录账号:");
            this.username = sc.nextLine();
            System.out.print("请输入SSH登录密码:");
            this.password = sc.nextLine();
        }
        this.encUsername = encrypt(this.username);
        this.encPassword = encrypt(this.password);
        FileWriter fw = new FileWriter(loginFile);
        fw.write("");
        fw.flush();
        fw.write(this.encUsername + " " + this.encPassword + "\n");
        fw.flush();
        fw.close();
    }

    @Override
    public void run(String... args) {
        try {
            JSch jsch = new JSch();

            ApplicationHome h = new ApplicationHome(getClass());
            String jarPath = h.getSource().getPath().replace('\\', '/');
            String dir = jarPath.substring(0, jarPath.lastIndexOf("/")) + "/";

            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_HHmmss");
            File path = new File(dir + formatter.format(new Date()) + "/");
            if (!path.exists()) {//如果文件夹不存在
                if (!path.mkdirs()) {
                    System.out.println("创建备份文件夹失败！！");
                    return;
                }
            }
            File logFile = new File(path + "/log.txt");
            if (!logFile.exists()) {
                if (!logFile.createNewFile()) {
                    System.out.println("创建" + logFile.getPath() + "失败！");
                    return;
                }
            }
            FileWriter logFileWriter = new FileWriter(logFile);

            File loginFile = new File(dir + "/login.ini");
            if (!loginFile.exists()) {
                if (!loginFile.createNewFile()) {
                    System.out.println("创建login.ini失败！");
                    logFileWriter.write("创建login.ini失败！\r\n");
                    return;
                }
            }
            System.out.println("读取SSH登录用户名、密码。");
            logFileWriter.write("读取SSH登录用户名、密码。\r\n");
            try {
                BufferedReader loginReader = new BufferedReader(new FileReader(loginFile));
                String loginStr = loginReader.readLine();
                String[] loginConfig = loginStr.split(" ");
                this.encUsername = loginConfig[0];
                this.encPassword = loginConfig[1];
                this.username = decrypt(this.encUsername);
                this.password = decrypt(this.encPassword);
            } catch (Exception el) {
                System.out.println("保存的SSH登录信息有误，请重新输入。");
                logFileWriter.write("保存的SSH登录信息有误，请重新输入。\r\n");
                inputLogin(loginFile);
            }
            System.out.println("获取SSH登录用户名、密码>>成功。");
            logFileWriter.write("获取SSH登录用户名、密码>>成功。\r\n");
            File iniFile = new File(dir + "dev.ini");
            BufferedReader iniReader = new BufferedReader(new FileReader(iniFile));
            ArrayList<String> configs = new ArrayList<>();
            String confStr;
            while ((confStr = iniReader.readLine()) != null) {
                configs.add(confStr);
            }
            iniReader.close();

            int error = 0;
            int success = 0;
            int count = 0;
            for (String devConf : configs) {
                String name = null;
                String ip = null;
                try {
                    String[] conf = devConf.split(" ");
                    name = conf[0];
                    ip = conf[1];
                    if (StringUtils.isBlank(name) || StringUtils.isBlank(ip)) {
                        System.out.println("(" + (count + 1) + "/" + configs.size() + ")配置有误:请检查dev.ini第" + (count + 1) + "行配置");
                        logFileWriter.write("(" + (count + 1) + "/" + configs.size() + ")配置有误:请检查dev.ini第" + (count + 1) + "行配置\r\n");
                        error++;
                        count++;
                        continue;
                    }
                    System.out.println("(" + (count + 1) + "/" + configs.size() + ")" + name + "_" + ip + ">>开始备份");
                    logFileWriter.write("(" + (count + 1) + "/" + configs.size() + ")" + name + "_" + ip + ">>开始备份\r\n");
                    Session session = jsch.getSession(this.username, ip, 22);
                    session.setPassword(this.password);
                    session.setConfig("StrictHostKeyChecking", "no");
                    session.connect(3000);
                    Channel channel = session.openChannel("shell");
                    channel.connect(3000);
                    InputStream in = channel.getInputStream();
                    OutputStream outputStream = channel.getOutputStream();
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println("dis cu");
                    printWriter.flush();
                    int moreCount = 0;
                    while (moreCount++ <= maxMore) {
                        printWriter.print(" ");
                        printWriter.flush();
                    }
                    printWriter.println("\r\nquit");
                    printWriter.flush();
                    printWriter.println("\r\nexit");
                    printWriter.flush();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
                    FileWriter configFileWriter = new FileWriter(path + "/" + name + "_" + ip + ".txt");
                    byte[] tmp = new byte[bufSize];
                    while (true) {
                        while (in.available() > 0) {
                            int i = in.read(tmp, 0, bufSize);
                            if (i < 0) break;
                            String buf;
                            buf = new String(tmp, 0, i);
                            //System.out.print(buf);
                            configFileWriter.write(buf);
                        }
                        if (channel.isClosed()) {
                            if (in.available() > 0) continue;
                            //System.out.println("exit-status: " + channel.getExitStatus());
                            break;
                        }
                    }
                    configFileWriter.flush();
                    configFileWriter.close();
                    reader.close();
                    printWriter.close();
                    channel.disconnect();
                    session.disconnect();
                    System.out.println("(" + (count + 1) + "/" + configs.size() + ")" + name + "_" + ip + ">>备份成功");
                    logFileWriter.write("(" + (count + 1) + "/" + configs.size() + ")" + name + "_" + ip + ">>备份成功\r\n");
                    success++;
                } catch (Exception e) {
                    System.out.print("(" + (count + 1) + "/" + configs.size() + ")" + name + "_" + ip + ">>备份失败:");
                    logFileWriter.write("(" + (count + 1) + "/" + configs.size() + ")" + name + "_" + ip + ">>备份失败:");
                    if (e.getMessage().contains("Auth fail")) {
                        System.out.println("登录失败,请检查设备SSH账号,或删除login.ini文件重新输入SSH登录账号密码！");
                        logFileWriter.write("登录失败,请检查设备SSH账号,或删除login.ini文件重新输入SSH登录账号密码！\r\n");
                    } else {
                        System.out.println(e);
                        logFileWriter.write(e + "\r\n");
                    }
                    error++;
                }
                count++;
                System.out.println();
                logFileWriter.write("\r\n");
            }
            System.out.println("备份完成:成功" + success + ",失败" + error + ",共" + count + "。\n本次备份运行日志请查看:" + logFile.getPath());
            logFileWriter.write("备份完成:成功" + success + ",失败" + error + ",共" + count + "。\r\n");
            logFileWriter.flush();
            logFileWriter.close();
            try {
                for (int i = exitTime; i >= 0; i--) {
                    Thread.sleep(1000);
                    System.out.print("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + i + "秒后退出...");
                }
            } catch (InterruptedException e) {
                System.out.println("\n");
                e.printStackTrace();
            }
            System.out.println("\n");
        } catch (Exception e) {
            try {
                e.printStackTrace();
                Thread.sleep(1000);
                System.out.println("按回车键退出...");
                System.in.read();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}
